package org.wicketskunkworks.gravatar;

import org.apache.wicket.Component;
import org.apache.wicket.IResourceListener;
import org.apache.wicket.Resource;
import org.apache.wicket.ResourceReference;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.MarkupStream;
import org.apache.wicket.markup.html.PackageResource;
import org.apache.wicket.markup.html.WebComponent;
import org.apache.wicket.markup.html.image.NonCachingImage;
import org.apache.wicket.markup.html.image.resource.LocalizedImageResource;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.util.value.ValueMap;

/**
 * An Image component displays a Gavatar image.
 * <p>
 * An except form the Gavatar site:<br/>
 * 
 * <pre>
 * A gravatar, or globally recognized avatar, is quite simply an image that 
 * follows you from site to site appearing beside your name when you do things.
 * </pre>
 * 
 * For details on Gavatar, see {@link http://www.gravatar.com/}.
 * <p>
 * For details of how Images load, generate and manage images, see
 * {@link LocalizedImageResource}.
 * 
 * @see NonCachingImage
 * 
 * @author Brill Pappin, Ported from code written by Jonathan Locke
 * 
 */
public class GravatarImage extends WebComponent implements IResourceListener {
	private static final long serialVersionUID = 1L;

	public static final String GAVATAR_FALLBACK_DEFAULT_IDENTICON = "identicon";
	public static final String GAVATAR_FALLBACK_DEFAULT_MONSTERID = "monsterid";
	public static final String GAVATAR_FALLBACK_DEFAULT_WAVATAR = "wavatar";
	public static final Integer GAVATAR_MINIMUM_SIZE = 1;
	public static final Integer GAVATAR_MAXIMUM_SIZE = 512;
	public static final Integer GAVATAR_DEFAULT_SIZE = 80;
	public static final Rating GAVATAR_DEFAULT_RATING = Rating.g;

	public enum Rating {
		g, pg, r, x
	}

	private final String GAVATAR_BASE_URL = "http://www.gravatar.com/avatar/";
	private final String GAVATAR_PARAM_SIZE = "s";
	private final String GAVATAR_PARAM_RATING = "r";
	// private final String GAVATAR_PARAM_FALLBACK = "d";

	private Integer size = GAVATAR_DEFAULT_SIZE;
	private Rating rating = GAVATAR_DEFAULT_RATING;
	private String fallback;
	private String email;

	/** The image resource this image component references */
	private final MutableLocalizedImageResource localizedImageResource = new MutableLocalizedImageResource(
	        this);

	/**
	 * This constructor can be used if you have a img tag that has a src that
	 * points to a PackageResource (which will be created and bind to the shared
	 * resources) Or if you have a value attribute in your tag for which the
	 * image factory can make an image.
	 * 
	 * @see org.apache.wicket.Component#Component(String)
	 */
	public GravatarImage(final String id, String email) {
		super(id);
		setEmail(email);
	}

	/**
	 * This constructor can be used if you have a img tag that has a src that
	 * points to a PackageResource (which will be created and bind to the shared
	 * resources) Or if you have a value attribute in your tag for which the
	 * image factory can make an image.
	 * 
	 * @see org.apache.wicket.Component#Component(String)
	 */
	public GravatarImage(final String id, String email, boolean useDefault) {
		super(id);
		setEmail(email);
		if (useDefault) {
			setImageResource(PackageResource.get(GravatarImage.class,
			        "silhouette-silver-100x100.gif"));
		}
	}

	/**
	 * Constructs an image from an image resourcereference. That resource
	 * reference will bind its resource to the current SharedResources.
	 * 
	 * If you are using non sticky session clustering and the resource reference
	 * is pointing to a Resource that isn't guaranteed to be on every server,
	 * for example a dynamic image or resources that aren't added with a
	 * IInitializer at application startup. Then if only that resource is
	 * requested from another server, without the rendering of the page, the
	 * image won't be there and will result in a broken link.
	 * 
	 * @param id
	 *            See Component
	 * @param resourceReference
	 *            The shared image resource
	 */
	public GravatarImage(final String id, String email,
	        final ResourceReference resourceReference) {
		this(id, email, resourceReference, null);
		setEmail(email);
	}

	/**
	 * Constructs an image from an image resourcereference. That resource
	 * reference will bind its resource to the current SharedResources.
	 * 
	 * If you are using non sticky session clustering and the resource reference
	 * is pointing to a Resource that isn't guaranteed to be on every server,
	 * for example a dynamic image or resources that aren't added with a
	 * IInitializer at application startup. Then if only that resource is
	 * requested from another server, without the rendering of the page, the
	 * image won't be there and will result in a broken link.
	 * 
	 * @param id
	 *            See Component
	 * @param resourceReference
	 *            The shared image resource
	 * @param resourceParameters
	 *            The resource parameters
	 */
	public GravatarImage(final String id, String email,
	        final ResourceReference resourceReference,
	        ValueMap resourceParameters) {
		super(id);
		setEmail(email);
		setImageResourceReference(resourceReference, resourceParameters);
	}

	/**
	 * Constructs an image directly from an image resource.
	 * 
	 * This one doesn't have the 'non sticky session clustering' problem that
	 * the ResourceReference constructor has. But this will result in a non
	 * 'stable' url and the url will have request parameters.
	 * 
	 * @param id
	 *            See Component
	 * 
	 * @param imageResource
	 *            The image resource
	 */
	public GravatarImage(final String id, String email,
	        final Resource imageResource) {
		super(id);
		setEmail(email);
		setImageResource(imageResource);
	}

	/**
	 * @see org.apache.wicket.Component#Component(String, IModel)
	 */
	public GravatarImage(final String id, String email, final IModel<?> model) {
		super(id, model);
	}

	/**
	 * @param id
	 *            See Component
	 * @param string
	 *            Name of image
	 * @see org.apache.wicket.Component#Component(String, IModel)
	 */
	public GravatarImage(final String id, String email, final String string) {
		this(id, email, new Model<String>(string));

	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public Integer getSize() {
		return size;
	}

	public void setSize(Integer size) {
		if (size < GAVATAR_MINIMUM_SIZE || size > GAVATAR_MAXIMUM_SIZE) {
			throw new IllegalArgumentException(
			        "Size must be between 1 and 512. " + size
			                + " is not valid.");
		}
		this.size = size;
	}

	public Rating getRating() {
		return rating;
	}

	public void setRating(Rating rating) {
		this.rating = rating;
	}

	public String getFallback() {
		return fallback;
	}

	public void setFallback(String fallback) {
		this.fallback = fallback;
	}

	/**
	 * @see org.apache.wicket.IResourceListener#onResourceRequested()
	 */
	public void onResourceRequested() {
		localizedImageResource.onResourceRequested();
	}

	/**
	 * @param imageResource
	 *            The new ImageResource to set.
	 */
	public void setImageResource(final Resource imageResource) {
		localizedImageResource.setResource(imageResource);
	}

	/**
	 * @param resourceReference
	 *            The shared ImageResource to set.
	 */
	public void setImageResourceReference(
	        final ResourceReference resourceReference) {
		localizedImageResource.setResourceReference(resourceReference);
	}

	/**
	 * @param resourceReference
	 *            The shared ImageResource to set.
	 * @param parameters
	 *            Set the resource parameters for the resource.
	 */
	public void setImageResourceReference(
	        final ResourceReference resourceReference, final ValueMap parameters) {
		localizedImageResource.setResourceReference(resourceReference,
		        parameters);
	}

	/**
	 * @see org.apache.wicket.Component#setDefaultModel(org.apache.wicket.model.IModel)
	 */
	@Override
	public Component setDefaultModel(IModel<?> model) {
		// Null out the image resource, so we reload it (otherwise we'll be
		// stuck with the old model.
		localizedImageResource.setResourceReference(null);
		localizedImageResource.setResource(null);
		return super.setDefaultModel(model);
	}

	/**
	 * @return Resource returned from subclass
	 */
	protected Resource getImageResource() {
		return localizedImageResource.getResource();
	}

	/**
	 * @return ResourceReference returned from subclass
	 */
	protected ResourceReference getImageResourceReference() {
		return localizedImageResource.getResourceReference();
	}

	/**
	 * @see org.apache.wicket.Component#initModel()
	 */
	@Override
	protected IModel<?> initModel() {
		// Images don't support Compound models. They either have a simple
		// model, explicitly set, or they use their tag's src or value
		// attribute to determine the image.
		return null;
	}

	/**
	 * @see org.apache.wicket.Component#onComponentTag(ComponentTag)
	 */
	@Override
	protected void onComponentTag(final ComponentTag tag) {
		checkComponentTag(tag, "img");
		super.onComponentTag(tag);
		final Resource resource = getImageResource();
		if (resource != null) {
			localizedImageResource.setResource(resource);
		}
		final ResourceReference resourceReference = getImageResourceReference();
		if (resourceReference != null) {
			localizedImageResource.setResourceReference(resourceReference);
		}
		localizedImageResource.setSrcAttribute(getGavatarUrl(), getFallback(),
		        tag);
	}

	private String getGavatarUrl() {
		if (getEmail() == null) {
			throw new IllegalStateException(
			        "Missing email property. Email must be set for a Gravitar Image.");
		}
		StringBuilder builder = new StringBuilder();
		builder.append(GAVATAR_BASE_URL);
		builder.append(GavatarMD5.md5Hex(getEmail()));
		builder.append(".jpg?");

		if (getSize() != null) {
			builder.append("&");
			builder.append(GAVATAR_PARAM_SIZE);
			builder.append("=");
			builder.append(getSize());
		}

		if (getRating() != null) {
			builder.append("&");
			builder.append(GAVATAR_PARAM_RATING);
			builder.append("=");
			builder.append(getRating());
		}

		return builder.toString();
	}

	/**
	 * @see org.apache.wicket.Component#getStatelessHint()
	 */
	@Override
	protected boolean getStatelessHint() {
		return (getImageResource() == null || getImageResource() == localizedImageResource
		        .getResource())
		        && localizedImageResource.isStateless();
	}

	/**
	 * @see org.apache.wicket.Component#onComponentTagBody(MarkupStream,
	 *      ComponentTag)
	 */
	@Override
	protected void onComponentTagBody(final MarkupStream markupStream,
	        final ComponentTag openTag) {
	}

}
